package drds.plus.sql_process.abstract_syntax_tree.expression.item.function;

import drds.plus.common.jdbc.Parameters;
import drds.plus.sql_process.abstract_syntax_tree.IExecutePlanVisitor;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.BindValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.extra_function.ExtraFunctionManager;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.extra_function.IExtraFunction;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.type.Type;
import drds.tools.$;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;


/**
 * 采取代理模式，将function处理转移到执行器中进行处理，比如处理分库的count/max等
 */
public class FunctionImpl<T extends Function> implements Function<T> {
    // count
    @Setter
    @Getter
    protected String functionName;
    @Setter
    @Getter
    protected String tableName;
    @Setter
    @Getter
    protected List argList = new ArrayList();//参数元素类型:Item  bind_value Query
    @Setter
    @Getter
    protected String alias;
    @Setter
    @Getter
    protected boolean distinct = false;
    // count(id)
    @Setter
    @Getter
    protected String columnName;
    @Setter
    @Getter
    protected IExtraFunction extraFunction;
    @Setter
    @Getter
    protected boolean isNot;
    @Setter
    @Getter
    protected boolean needDistinctArg = false;
    @Setter
    @Getter
    protected Long correlateFilterId = 0L;

    public String getFunctionName() {
        return functionName;
    }

    public void setFunctionName(String functionName) {
        this.functionName = functionName;

    }

    public void setType(Type type) {
        throw new UnsupportedOperationException();
    }

    public String getAlias() {
        return alias;
    }

    public String getTableName() {
        return tableName;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setAlias(String alias) {
        this.alias = alias;

    }

    public void setTableName(String tableName) {
        this.tableName = tableName;

    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;

    }

    /**
     * 当前对象和传入的参数对象都是先比较别名再比较列名
     */
    public boolean isSameColumnName(Item item) {
        String cn1 = this.getColumnName();
        if ($.isNotNullAndNotEmpty(this.getAlias())) {
            cn1 = this.getAlias();
        }

        String cn2 = item.getColumnName();
        if ($.isNotNullAndNotEmpty(item.getAlias())) {
            cn2 = item.getAlias();
        }

        return cn1.equals(cn2);
    }

    public String getFullName() {
        return this.getColumnName();
    }

    public boolean isDistinct() {
        return distinct;
    }

    public boolean isNot() {
        return isNot;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;

    }

    public void setIsNot(boolean isNot) {
        this.isNot = isNot;

    }

    public List getArgList() {
        return argList;
    }

    public void setArgList(List list) {
        this.argList = list;

    }

    public boolean isNeedDistinctArg() {
        return needDistinctArg;
    }

    public void setNeedDistinctArg(boolean needDistinctArg) {
        this.needDistinctArg = needDistinctArg;

    }

    public T copy() {
        Function function = ObjectCreateFactory.createFunction();
        function.setFunctionName(getFunctionName());
        function.setAlias(this.getAlias());
        //
        function.setTableName(this.getTableName());
        function.setColumnName(this.getColumnName());
        function.setDistinct(this.isDistinct());
        function.setIsNot(this.isNot());
        function.setCorrelateSubQueryItemId(this.getCorrelateSubQueryItemId());

        function.setNeedDistinctArg(this.isNeedDistinctArg());
        if (getArgList() != null) {
            List<Object> objectList = new ArrayList<Object>(getArgList().size());
            for (Object arg : getArgList()) {
                if (arg instanceof Item) {
                    objectList.add(((Item) arg).copy());
                } else if (arg instanceof BindValue) {
                    objectList.add((((BindValue) arg).copy()));
                } else if (arg instanceof Query) {
                    objectList.add((((Query) arg).deepCopy()));
                } else {
                    objectList.add(arg);
                }

            }
            function.setArgList(objectList);
        }
        return (T) function;
    }

    /**
     * 复制一下function属性
     */
    protected void copy(Function function) {
        function.setFunctionName(getFunctionName());
        function.setAlias(this.getAlias());
        //
        function.setTableName(this.getTableName());
        function.setColumnName(this.getColumnName());
        function.setDistinct(this.isDistinct());
        function.setIsNot(this.isNot());
        function.setCorrelateSubQueryItemId(this.getCorrelateSubQueryItemId());

        if (getArgList() != null) {
            List<Object> objectList = new ArrayList<Object>(getArgList().size());
            for (Object arg : getArgList()) {
                if (arg instanceof Item) {
                    objectList.add(((Item) arg).copy());
                } else if (arg instanceof BindValue) {
                    objectList.add((((BindValue) arg).copy()));
                } else if (arg instanceof Query) {
                    objectList.add((((Query) arg).deepCopy()));
                } else {
                    objectList.add(arg);
                }

            }
            function.setArgList(objectList);
        }
    }

    public void accept(IExecutePlanVisitor executePlanVisitor) {
        executePlanVisitor.visit(this);
    }

    public int compareTo(Object o) {
        throw new UnsupportedOperationException();
    }

    public T assignment(Parameters parameters) {
        if (getArgList() != null) {
            List<Object> argList = getArgList();
            int index = 0;
            for (Object arg : getArgList()) {
                if (arg instanceof Item) {
                    argList.set(index, ((Item) arg).assignment(parameters));
                } else if (arg instanceof BindValue) {
                    argList.set(index, ((BindValue) arg).assignment(parameters));
                } else {
                    argList.set(index, arg);
                }
                index++;
            }
            this.setArgList(argList);
        }

        return (T) this;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.getColumnName());
        if (this.getAlias() != null) {
            sb.append(" setAliasAndSetNeedBuild ").append(this.getAlias());
        }
        return sb.toString();
    }

    public IExtraFunction getExtraFunction() {
        if (extraFunction == null) {
            extraFunction = ExtraFunctionManager.getExtraFunction(getFunctionName());
            extraFunction.setFunction(this);// 不可能为null
        }
        return extraFunction;
    }

    public void setExtraFunction(IExtraFunction extraFunction) {
        this.extraFunction = extraFunction;
    }

    public FunctionType getFunctionType() {
        return getExtraFunction().getFunctionType();
    }

    public Type getType() {
        return getExtraFunction().getReturnType();
    }

    public Long getCorrelateSubQueryItemId() {
        return correlateFilterId;
    }

    public void setCorrelateSubQueryItemId(Long subQueryCorrelateFilterId) {
        this.correlateFilterId = subQueryCorrelateFilterId;

    }

    public boolean isAutoIncrement() {
        return false;
    }

    public void setAutoIncrement(boolean autoIncrement) {
        //
    }

    /**
     * 这个方法不要被自动修改！ 在很多地方都有用到。
     */

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((argList == null) ? 0 : argList.hashCode());
        result = prime * result + ((columnName == null) ? 0 : columnName.hashCode());
        result = prime * result + (distinct ? 1231 : 1237);
        result = prime * result + ((functionName == null) ? 0 : functionName.hashCode());
        result = prime * result + (isNot ? 1231 : 1237);
        result = prime * result + (needDistinctArg ? 1231 : 1237);
        result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());
        return result;
    }

    /**
     * 这个方法不要被自动修改！ 在很多地方都有用到。
     */

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        FunctionImpl other = (FunctionImpl) obj;

        if (((alias == null && other.alias != null)) || ((alias != null && other.alias == null))) {
            return false;
        }
        // setAliasAndSetNeedBuild 都不为空的时候，进行比较，如果不匹配则返回false,其余时候都跳过alias匹配
        if (alias != null && other.alias != null) {
            if (!alias.equals(other.alias)) {
                return false;
            }
        }

        if (argList == null) {
            if (other.argList != null) {
                return false;
            }
        } else if (!argList.equals(other.argList)) {
            return false;
        }
        if (columnName == null) {
            if (other.columnName != null) {
                return false;
            }
        } else if (!columnName.equals(other.columnName)) {
            return false;
        }
        if (distinct != other.distinct) {
            return false;
        }
        if (functionName == null) {
            if (other.functionName != null) {
                return false;
            }
        } else if (!functionName.equals(other.functionName)) {
            return false;
        }
        if (isNot != other.isNot) {
            return false;
        }
        if (needDistinctArg != other.needDistinctArg) {
            return false;
        }
        if (tableName == null) {
            return other.tableName == null;
        } else
            return tableName.equals(other.tableName);
    }
}
